#include "buffer.h" 

NS_BEGIN(sk)

buffer::buffer(size_t buffer_size) : capacity_(buffer_size),
                                     windex_(0), rindex_(0) {
    this->address_ = malloc(buffer_size);
    assert(this->address_);
}

buffer::buffer(void *data, size_t len) {
    this->capacity_ = len;
    this->windex_ = 0;
    this->rindex_ = 0;
    this->address_ = malloc(this->capacity_);
    assert(this->address_);
    
    memcopy(this->address_, data, len);
    this->windex_ += len;
}

buffer::~buffer() {
    if (this->address_) {
        free(this->address_);
        this->address_ = nullptr;
    }

    this->capacity_ = 0;
    this->windex_ = 0;
    this->rindex_ = 0;
}

void buffer::commit(size_t n) {
    assert(this->capacity_>= this->windex_);
    assert(this->capacity_ >= this->windex_ + n);
    this->windex_ += n;
}

void buffer::consume(size_t n) {
    size_t sn = this->size();
    assert(sn >= n);

    this->rindex_ += n;

    if (n >= sn) {
        this->windex_ = 0;
        this->rindex_ = 0;
    }
}

void *buffer::prepare(size_t n) {
    return sk::byte_offset<void>(this->address_, this->windex_);
}

void ensure_space(size_t len) {
    assert(this->capacity_ >= this->windex_); 

    if (this->capacity_ - this->windex_ >= n) return;

    size_t sn  = this->size();
    if (this->capacity_ - sn >= n) { 
        if (sn > 0)
            memmove(this->address_, sk::byte_offset<void>(this->address_, this->rindex_), sn);
        this->rindex_ = 0;
        this->windex_ = this->rindex_ + sn;
    } 
    
    {
        void *new_addr = malloc(1.5 * (this->capacity_ + n));
        if (new_addr == nullptr)  return nullptr;

        memcpy(new_addr, this->address_, this->capacity_);
        free(this->address_);
        this->address_ = new_addr;
        this->capacity_ = 1.5 * (this->capacity_ + n);
    }

}

NS_END(sk)
